package com.blackrock.das.service;

import static com.blackrock.das.enums.DataType.AUM;
import static com.blackrock.das.enums.DataType.FUND_PERFORMANCE;
import static com.blackrock.das.enums.DataType.INDEX_PERFORMANCE;
import static com.blackrock.das.enums.DataType.NAV;
import static com.blackrock.das.enums.DataType.SHARES_IN_ISSUE;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.blackrock.das.enums.DataType;
import com.blackrock.das.enums.Resolution;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;

/**
 * The Class PortfolioServiceImpl.
 */
@Service
public class PortfolioServiceImpl implements PortfolioService {

    /** The Constant LOGGER. */
    private final static Logger LOGGER = Logger.getLogger(PortfolioServiceImpl.class);

    /** The Constant CQL_GET_PORTFOLIO_STATIC_DATA. */
    private static final String CQL_GET_PORTFOLIO_STATIC_DATA = "SELECT * FROM PortfolioStaticData WHERE isin = '%s';";

    /** The Constant CQL_GET_PORTFOLIOS_ISIN. */
    private static final String CQL_GET_PORTFOLIOS_ISIN = "SELECT isin FROM PortfolioStaticData;";

    /** The cassandra service. */
    @Autowired
    private CassandraService cassandraService;

    /*
     * (non-Javadoc)
     * 
     * @see com.blackrock.das.service.TimeSeriesDataService#getPorfoliosIsin()
     */
    @Override
    public List<String> getPorfoliosIsin() {
        List<String> isins = Collections.emptyList();

        ResultSet queryResult = cassandraService.executeQuery(CQL_GET_PORTFOLIOS_ISIN);
        if (queryResult != null) {
            isins = new ArrayList<String>();

            for (Row row : queryResult.all()) {
                isins.add(row.getString("isin"));
            }
        }

        return isins;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.blackrock.das.service.StaticDataService#getStaticData(java.lang.String)
     */
    @Override
    public Map<String, Object> getStaticData(String isin) {
        Map<String, Object> staticData = Collections.emptyMap();

        ResultSet queryResult = cassandraService.executeQuery(String.format(CQL_GET_PORTFOLIO_STATIC_DATA, isin));
        Row row = queryResult.one();
        if (row != null) {
            staticData = new HashMap<String, Object>();

            staticData.put("isin", row.getString("isin"));
            staticData.put("ticker", row.getString("ticker"));
            staticData.put("fundName", row.getString("fundName"));
            staticData.put("benchmarkName", row.getString("benchmarkName"));
            staticData.put("inceptionDate", row.getDate("inceptionDate"));
        }

        return staticData;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.blackrock.das.service.TimeSeriesDataService#getTimeSeriesData(java.lang.String, java.util.Date, java.util.Date,
     * com.blackrock.das.enums.Resolution)
     */
    @Override
    public Map<String, Map<String, BigDecimal>> getTimeSeriesData(String isin, Date startDate, Date endDate, Resolution resolution) {
        return getTimeSeriesData(isin, startDate, endDate, resolution, new HashSet<DataType>(Arrays.asList(DataType.values())));
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.blackrock.das.service.pricing.NavService#getNavHistory(java.lang.Integer, java.lang.String)
     */
    @Override
    public Map<String, Map<String, BigDecimal>> getTimeSeriesData(String isin, Date startDate, Date endDate, Resolution resolution,
            Set<DataType> dataTypes) {

        Map<String, Map<String, BigDecimal>> timeSeriesData = new LinkedHashMap<String, Map<String, BigDecimal>>();
        Session session = cassandraService.getSession();
        ResultSet queryResult;

        String cql = buildTimeSeriesDataCql(isin, startDate, endDate, dataTypes, resolution);
        PreparedStatement preparedQuery = cassandraService.getSession().prepare(cql);
        queryResult = session.execute(preparedQuery.bind(isin, startDate, endDate));

        for (Row row : queryResult.all()) {

            Map<String, BigDecimal> data = new HashMap<String, BigDecimal>();
            timeSeriesData.put(String.valueOf(row.getDate("date").getTime()), data);

            if (dataTypes.contains(FUND_PERFORMANCE)) {
                data.put(FUND_PERFORMANCE.getColumnName(), row.getDecimal("fundReturn"));
            }
            if (dataTypes.contains(INDEX_PERFORMANCE)) {
                data.put(INDEX_PERFORMANCE.getColumnName(), row.getDecimal("benchReturn"));
            }
            if (dataTypes.contains(AUM)) {
                data.put(AUM.getColumnName(), row.getDecimal("AUM"));
            }
            if (dataTypes.contains(SHARES_IN_ISSUE)) {
                data.put(SHARES_IN_ISSUE.getColumnName(), row.getDecimal("sharesInIssue"));
            }
            if (dataTypes.contains(NAV)) {
                data.put(NAV.getColumnName(), row.getDecimal("NAV"));
            }
        }

        return timeSeriesData;
    }

    /**
     * Builds the time series data cql.
     *
     * @param isin the isin
     * @param startDate the start date
     * @param endDate the end date
     * @param dataTypes the data types
     * @param resolution the resolution
     * @return the string
     */
    private String buildTimeSeriesDataCql(String isin, Date startDate, Date endDate, Set<DataType> dataTypes, Resolution resolution) {
        StringBuilder cql = new StringBuilder("SELECT date, ");

        if (resolution == null) {
            resolution = Resolution.DAILY;
        }

        for (Iterator<DataType> it = dataTypes.iterator(); it.hasNext();) {
            DataType dataType = it.next();
            cql.append(dataType.getColumnName());
            if (it.hasNext()) {
                cql.append(", ");
            }
        }

        cql.append(" FROM PortfolioTimeSeries ");

        if ((isin != null) || (startDate != null) || (endDate != null) || (dataTypes != null) || (resolution != null)) {
            cql.append("WHERE ");
            if (isin != null) {
                cql.append("isin = ? ");
            }
            if (startDate != null) {
                cql.append("AND date >= ? ");
            }
            if (endDate != null) {
                cql.append("AND date <= ? ");
            }

            if (resolution != null) {
                switch (resolution) {
                case WEEKLY:
                    cql.append("AND weekly = true ");
                    break;
                case MONTHLY:
                    cql.append("AND monthly = true ");
                    break;
                default:
                    break;
                }
            }
        }

        LOGGER.debug(cql);
        return cql.toString();
    }

}
